from manimlib.imports import *
import time
#TODO :提高写字幕的效率,通过一些批量手段
def hasTex(str):#例如str="床前$x^2$明月光"
    #专门用来处理Tex包含的字符串
    TextPos=0
    a=0#
    output=VGroup()
    dollarPos=str.find("$")#
    if(dollarPos>=0):
        while(dollarPos>=0 and TextPos>=0):#
            # print(TextPos,dollarPos)
            if(dollarPos>TextPos):#
                # print("1",str[TextPos:dollarPos])
                b=Text(str[TextPos:dollarPos])
                output.add(b)  # add
            dollarPos=dollarPos+1#3
            TextPos=str.find("$",dollarPos)#6
            if(TextPos>=0 ):
                # print("2",str[dollarPos:TextPos])
                b=TexMobject(str[dollarPos:TextPos])
                b.scale(2)
                output.add(b)
            TextPos=TextPos+1 #9 18
            dollarPos = str.find("$",TextPos)#13 21
            if(dollarPos==len(str)-1 or dollarPos == -1):#到达边界或找不到
                # print("3",str[TextPos:])
                b=(Text(str[TextPos:]))
                output.add(b)
                break
    else:
        output.add(Text(str))
    # print(f"outputlen:{len(output)}")
    b=output[0]
    if(len(output)>1):
        for i in output[1:]:
            i.next_to(b,RIGHT, buff=0.2)
            b=i
    return output
def TexText(*key):
    output=VGroup()
    a=(hasTex(key[0]))
    output.add(a)
    if(len(key)>1):
        for i  in key[1:]:
            b=hasTex(i)
            b.next_to(a,RIGHT, buff=0.1)
            output.add(b)
            a=b
    output.scale(.5).move_to([0,0,0])
    return output
def TexTextflat(*key):
    output=VGroup()
    a=key[0]
    if("$" in a):
        output.add(TexMobject(a[1:len(a)-1]))
        output[-1].scale(2)
    else:
        output.add(Text(a))
    if(len(key)>1):
        for i  in key[1:]:
            if ("$" in i):
                output.add(TexMobject(i[1:len(i)-1]))
                output[-1].scale(2)
            else:
                output.add(Text(i))
    a=output[0]
    if(len(output)>1):
        for i in output[1:]:
            i.next_to(a,RIGHT, buff=0)
            i.align_to(a[0][-1],DOWN)
            a=i
    output.scale(.5).move_to([0,0,0])
    return output
def timebox(fun):
    开始时间 = time.time()
    print(f"time begin at {开始时间}")
    fun()
    结束时间 = time.time()
    print(f"执行用时{round((结束时间 - 开始时间) / 60, 2)}分钟")
littleup=UP*.5
littledown=DOWN*.5
'''
为什么要这么设计父类,
他要包括一些公共使用的属性和方法,减少垃圾文件的产生,增加编译速度和代码速度
为什么用def construct(self):而不用 def __init_
这实际上因为construct是一个自定义函数,不是__init__函数
__init__函数中用自定义函数会报错,出现一些麻烦的事情
注意,VGroup是一个数组,所以如果Vgroup里面套了数学公式,想要得到数学符号,哪怕Vgroup里面只有一个元素
也需要Vgroup[0][0][choice]
注意,通过VGroup包装的数学公式,位置通常是不确定的,需要初始化.
'''
class 辅助Scene(Scene):
    def 变换(self,来,去,消=None,复=True):
        '''
        用于文字的运动
        :param 来: from
        :param 去: to
        :param 消: fadeout
        :param 复: fromcopy or single transform
        :return:
        '''
        播=TransformFromCopy if 复 else Transform
        if 消:
            self.play(播(来,去),FadeOut(消))
        else:
            self.play(播(来, 去))
    def 水印(self):
        水印_=Text("b站-拾吾同学",stroke_width=10).scale(2).set_opacity(0.02)
        # self.play()
        self.add(水印_)

class 微分算子源(辅助Scene):
    def 微分算子12(self):
        '''
        把文本的位置关系放在这个函数里算好,再导出去.
        这个函数做的非常差,但是不想改了,一坨黑历史,就是遵从这样的规则就行了
        :return: 用来调用的部件.
        '''
        textBoxDict={
                "form": TexText(r"$Df="  # 0 1 2
                                r"f'"  # 3 4 
                                r"(f')'"  # 5 6 7 8 9
                                r"f''"  # =10 11 12
                                r"DD"  # 13 14
                                r"D\cdot D"  # 15 16 17
                                r"D^2"  # 18 19
                                r"(f)^{''\cdots'}"  # ( f ) ' ' . . . '  20 21 22 23 24 25 26 27 28
                                r"f^{(n)}"  # f ( n ) 29 30 31 32
                                r"DD\cdots D"  # D D . . . D  33 34 35 36 37 38
                                r"D^n$"  # 39 40
                                ),
                "title": TexText("微分算子的", "定义", ":"),
                "title2": TexText("算子的", "乘法性质"),
                "title3": TexText("算子的", "加法性质"),
                "移动求导符号": TexText("移动求导符号$'$,到$f$前面,变成$D$"),
                "这就是微分算子": TexText("这个$D$就是", "微分算子"),
                "是求导运算": TexText("他表示对$f$的", "求导运算", ",是一个", "运算符号"),
                "同时又像数": TexText("但这个符号又像", "数", "一样,本身也有一些", "运算"),
                "算子乘法运算1": TexText("比如我们对$f'$继续求导,", "则等式右边变成"),
                "算子乘法运算2": TexText("那么左边就出现了两个$D$,亦即算子的复合", ",他们可以看作彼此相乘,", "记为"),
                "算子乘法运算3": TexText("所以如果有$n$阶求导,", "那就有$n$个$D$相乘,", "写作"),
        }
        title = textBoxDict["title"]
        title2 = textBoxDict["title2"]
        title3 = textBoxDict["title3"]
        form = textBoxDict["form"][0][0][0]
        移动求导符号 = textBoxDict["移动求导符号"]
        这就是微分算子 = textBoxDict["这就是微分算子"]
        是求导运算 = textBoxDict["是求导运算"]
        同时又像数 = textBoxDict["同时又像数"]
        算子乘法运算1 = textBoxDict["算子乘法运算1"]
        算子乘法运算2 = textBoxDict["算子乘法运算2"]
        算子乘法运算3 = textBoxDict["算子乘法运算3"]
        算子乘法运算x=VGroup(算子乘法运算1, 算子乘法运算2, 算子乘法运算3)
        g = {
            "form":form,
            "f'": VGroup(form[3:5]),
            # "f'copy":VGroup(form[3:5].copy()),
            "Df=": VGroup(form[0:3]),
            "=left": VGroup(form[0:2]),
            "=right": VGroup(form[3:]),
            "''": VGroup(form[11:13]),
            "nexttorect1": VGroup(移动求导符号, 是求导运算, 同时又像数, 算子乘法运算x),
            "setcoloryellow": VGroup(这就是微分算子[1], 是求导运算[1], 是求导运算[3], 同时又像数[1], 同时又像数[3], title[1], title2[1],
                                     title3[1]),
            "文本":VGroup(移动求导符号,这就是微分算子,是求导运算,同时又像数,算子乘法运算1,算子乘法运算2,算子乘法运算3),
            "titles": VGroup(title, title2, title3),
            "Tobject": 移动求导符号,
            "Dform": VGroup(form[0], form[13:15], form[15:18], form[18:20], form[33:39], form[39:]),
            "'form": VGroup(form[3:5], form[5:10], form[10:13], form[20:29], form[29:33]),
            "算子乘法运算x" :算子乘法运算x
        }
        # 位置颜色大小,注意位置前后关系,颜色无所谓
        g["titles"].to_corner(LEFT + UP)
        form[2].move_to([0, 0, 0])
        g["=left"].next_to(form[2], LEFT, buff=.2)
        g["=right"].next_to(form[2], RIGHT, buff=.2)
        g["setcoloryellow"].set_color(YELLOW)
        for el in g["Dform"]:
            el.next_to(form[1], LEFT, buff=.1)
        for el in g["'form"]:
            el.next_to(form[2], RIGHT, buff=.1)
        g["'form"][-1].align_to(form[1], DOWN)
        g["'form"][-2].align_to(form[1], DOWN)
        # 助记图形
        g["rect1"] = SurroundingRectangle(form[4])
        g["rect2"] = SurroundingRectangle(form[0])
        # 所需位置
        这就是微分算子.next_to(g['rect2'], DOWN, buff=.2).scale(.5)
        g["nexttorect1"].next_to(g["rect1"], UP, buff=.2).scale(.5)
        # 位置设置
        return g
        pass
    def 微分算子3(self):
        '''
        主题:
        算子的乘法性质:数乘分配
        台词:
        通过上面的介绍,我们知道D与D之间可以幂乘运算,(播放一下分开与合并的动画)
        下面来看D与函数之间的分配律(新增一个等号),首先把等式右边写成(公式生成)
        可知右边是两个函数相加并求导,根据求导运算法则我们知道右边等于(向下分裂复制)
        又根据微分算子的定义,于是我们又知道左边等于(第二行等号)
        现在我们回到第一行式子,可以发现
        等式右边还可以把$h+g$整体看成一个函数求导(把h+g变成f写在中间,再变回去)
        于是根据微分算子的定义,又可以得到左边的结果,
        并且由于两行式子的右边相等,于是左边也相等(写成一个新的式子)
        因此我们就得到了D与函数之间的分配律关系,
        他还能拓展到n次求导(配一个动画),以及多个函数的线性组合求导(再配一个动画)
        (过长的文字可以通过上移,再写,最后集体消除,尝试使用局部渲染,一句一句话来写,先设计文本动画,再设计公式的)
        '''
        g1=self.微分算子12()
        g1["titles"].add(Text("数乘分配律"))
        g1["titles"][-1]
        g1["titles"][-1].set_color(GREEN).next_to(g1["titles"][1], RIGHT, buff=0).scale(.5)
        g2={
            "算子3":{
                "全文":VGroup(),
                "散句":{
                    #段落1
                    "总结前文": TexText("通过上面的介绍.", "我们知道$D$与$D$之间可以幂乘运算"),
                    "数乘分配引入": TexText("下面来看$D$与函数之间的分配律.", "首先在等式右边写成"),
                    # 段落2
                    "两函相加求导": TexText("可知右边是两个函数相加并求导.", "根据求导运算法则我们知道右边等于"),
                    "相加求导算子定义": TexText("又根据微分算子的定义.", "于是我们又知道左边等于"),
                    # 段落3
                    "回到第一行": TexText("现在我们回到第一行式子.", "可以发现"),
                    "两函看一函": TexText("等式右边还可以把$h+g$整体.", "看成一个函数求导"),
                    "一函求导算子定义": TexText("于是根据微分算子的定义.", "又可以得到左边的结果"),
                    "两行相等": TexText("并且由于两行式子的右边相等.", "于是左边也相等"),
                    # 段落4
                    "D与函数的分配": TexText("因此我们就得到了.", "$D$与函数之间的分配律关系"),
                    "分配推广": TexText("他还能拓展到$n$次求导", "以及多个函数的线性组合求导", "于是有")
                },
                "段落":{
                    "1":VGroup(),"2":VGroup(),"3":VGroup(),"4":VGroup()
                },
                "式子":{
                    "综合": TexText("$D(h+g)=(h+g)'$",#0
                                  "$Dh+Dg=h'+g'$",#1
                                  "$f$",#2
                                  "$D(h+g)=Dh+Dg$",#3
                                  "$D^{n}(h+g)$",#4
                                  "$D^{n}h+D^{n}g$",#5
                                  r"$(a_{1}f_{1}+a_{2}f_{2}+\cdots+a_{m}f_{m})^{(n)}$",#6
                                  r"$(\sum_{k=1}^{m}a_{k}f_{k})^{(n)}$",#7
                                  r"$=D^{n}\sum_{k=1}^{m}a_{k}f_{k}$",#8
                                  r"$((a_{1}f_{1})^{(n)}+(a_{2}f_{2})^{(n)}+\cdots+(a_{m}f_{m})^{(n)})$",#9
                                  r"$(D^{n}(a_{1}f_{1})+D^{n}(a_{2}f_{2})+\cdots+D^{n}(a_{m}f_{m}))$",#10
                                  r"$=\sum_{k=1}^{m}D^{n}a_{k}f_{k}$",#11
                                  ),
                }
            }
        }
        #安排字幕的位置,大小,颜色,对齐
        temp = TexText("$=$").move_to([0, 0, 0])
        散句 = g2["算子3"]["散句"]
        段落 = g2["算子3"]["段落"]
        全文 = g2["算子3"]["全文"]
        for i in 散句.values():
            i.scale(.5).move_to(g1["文本"][6]).align_to(g1["文本"][6],LEFT).align_to(g1["文本"][6],DOWN)
            全文.add(i)
        段落["1"].add(散句["总结前文"],散句["数乘分配引入"])
        段落["2"].add(散句["两函相加求导"], 散句["相加求导算子定义"])
        段落["3"].add(散句["回到第一行"], 散句["两函看一函"],散句["一函求导算子定义"],散句["两行相等"])
        段落["4"].add(散句["D与函数的分配"], 散句["分配推广"])
        f=散句["两函看一函"][0][1][0]
        high=f[0].get_bottom()
        low=f[-1].get_bottom()
        f.shift(DOWN*(high[1]-low[1])/2)
        g2["算子3"]["着绿色"]=VGroup(
            散句["总结前文"][1][4][-4:],
            散句["数乘分配引入"][0][2][-4:],
            散句["分配推广"][2],
            散句["两函相加求导"][1][0][2:8]).set_color(GREEN)

        #安排公式位置
        综合 = g2["算子3"]["式子"]["综合"]
        综合[0][0][0][6].move_to([0, 0, 0])
        # D(h+g)
        综合[0][0][0][0:6].next_to(综合[0][0][0][6],LEFT,buff=.1)
        # (h+g)'
        综合[0][0][0][-6:].next_to(综合[0][0][0][6], RIGHT, buff=.1).align_to(综合[0][0][0][0:6],DOWN)
        # h'+g'
        综合[1][0][0][-5:].next_to(综合[0][0][0][-6:],DOWN,buff=.2).align_to(综合[0][0][0][-6:],LEFT)

        综合[1][0][0][-6].next_to(综合[1][0][0][-5:],LEFT,buff=.1)
        # Dh+Dg
        综合[1][0][0][0:5].next_to(综合[1][0][0][-6],LEFT,buff=.1)
        综合[1][0][0][-2].align_to(综合[1][0][0][4],DOWN)
        # f
        综合[2][0][0][0].move_to(综合[0][0][0][-6:])
        综合[3].move_to([0, 0, 0])
        综合[4].next_to(综合[3][0][0][6],LEFT,buff=.1)
        综合[5].next_to(综合[3][0][0][6], RIGHT, buff=.1)
        综合[6].next_to(综合[3],DOWN, buff=.8).align_to(综合[3],LEFT)
        综合[7].next_to(综合[3],DOWN, buff=.8).align_to(综合[3],LEFT)
        综合[9].next_to(综合[3], DOWN, buff=.8).align_to(综合[3], LEFT)
        综合[10].scale(0.9).next_to(综合[3], DOWN, buff=.8).align_to(综合[3], LEFT)
        综合[8].next_to(综合[7],RIGHT,buff=.1)
        综合[11].next_to(综合[8], RIGHT, buff=.1)
        g2["算子3"]["式子"]["(n)组合"]= VGroup(
            综合[9][0][0][8],
            综合[9][0][0][18],
            综合[9][0][0][32],
        )


        #安排上下帧的交替
        g2["上一帧剩余"]=VGroup(g1["文本"][6],g1["titles"][1],g1["form"][1:3],g1["'form"][4],g1["Dform"][5]
        )
        g2["Dn公式"]=g2["上一帧剩余"][2:].copy().scale(.5).next_to(g1["titles"],DOWN, buff=0.2).align_to(g1["titles"],LEFT)
        g2["算子3"]["最终公式"] = VGroup(综合[7], 综合[8],综合[11]).copy().scale(.5).next_to(g2["Dn公式"],DOWN, buff=.2).align_to(g1[
                                                                                                                  "titles"],LEFT)
        # print(len(g2["总结前文"][1]))#用来测试是不是含有那么多项,确定是否正常
        g2["圈圈"]= VGroup(
            SurroundingRectangle(g1["titles"][1]),#0
            SurroundingRectangle(g1["titles"][3]),#1
            SurroundingRectangle(g2["上一帧剩余"][2:]),#2
            SurroundingRectangle(g2["Dn公式"]),#3
            SurroundingRectangle(VGroup(综合[0][0][0][-6:],综合[1][0][0][-5:])),#4
            SurroundingRectangle(VGroup(综合[0][0][0][0:6], 综合[1][0][0][0:5])),#5
            SurroundingRectangle(综合[8][0][0][1:3]),#6
            SurroundingRectangle(综合[11][0][0][6:8])#7
        )
        return {**g1,**g2}
    def 微分算子4(self):
        '''
        段落1
        我们将数乘法则与乘积法则结合.就能运算如下的式子(D(Df+f))+f=(D^2+D+E)f.
        其中等式右侧的E是恒等算子,表示将f映到f,在不混淆的情况下也可写作1
        可见微分算子是满足线性运算的,
        段落2
        下面我们将不同求导次数的函数加在一起,借之前的结论我们可以得到这样的等式,
        从形式上看,他很像n次多项式,也可以简单证明,微分算子就是一个交换整环,
        即他同构于多项式
        '''
        pass

    def 微分算子5(self):
        '''
        段落1
        为了证明微分算子是交换整环
        '''
        pass


class 微分算子1(微分算子源):
    def work(self):
        g=super().微分算子12()
        self.水印()
        这就是微分算子=g["文本"][1]
        是求导运算=g['文本'][2]
        同时又像数=g["文本"][3]
        form=g['form']
        title=g['titles'][0]
        #助记图形
        rect1=g["rect1"]
        rect2=g["rect2"]
        self.play(Write(title),run_time=2)
        # 移动求导符号
        self.play(Write(g["f'"]))
        self.play(ShowCreation(rect1))
        self.play(Write(g["Tobject"]))#引用移动求导符号
        self.wait(2)
        self.play(
            TransformFromCopy(g["f'"],g["Df="]),#TransformFromCopy将a的副本变换到b,到时可以将b 以FadeOut消除
            TransformFromCopy(rect1,rect2)
        )
        self.wait()
        # 这就是微分算子
        self.play(Write(这就是微分算子))
        self.wait(3)
        # 是求导运算
        self.play( Transform(g["Tobject"],是求导运算))
        self.wait(2)
        self.play( Transform(g["Tobject"],同时又像数))
        self.wait(2)
    def construct(self):
        timebox(self.work)
        pass
class 微分算子2(微分算子源):
    def work(self):
        g = super().微分算子12()
        self.水印()
        form = g["form"]
        算子乘法运算x=g["算子乘法运算x"]
        上方文字 = g["文本"][3]
        title = g["titles"][0]
        title2 = g["titles"][1]
        下方文字 = g["文本"][1]
        等号右边 = g["f'"]
        方块= VGroup(g['rect1'],g['rect2'])
        # 前景布置
        self.add(title, 上方文字, 下方文字, 方块, form[0:5])

        #开始动画
        self.变换(上方文字, 算子乘法运算x[0][0], 上方文字)
        self.变换(title, title2, 复=False)
        self.wait(2)
        self.变换(算子乘法运算x[0][0], 算子乘法运算x[0][1])
        self.play(Transform(等号右边,g["'form"][1]),FadeOut(方块),FadeOut(下方文字))
        self.wait()
        self.play(Transform(等号右边,g["'form"][2]))
        self.wait()
        self.变换(算子乘法运算x[0][1], 算子乘法运算x[1][0], 算子乘法运算x[0])
        等号左边 = g["Dform"]
        self.变换(等号右边, 等号左边[1], 等号左边[0])
        self.wait()
        self.变换(算子乘法运算x[1][0], 算子乘法运算x[1][1])
        self.wait()
        self.play(Transform(等号左边[1],等号左边[2]))
        self.wait()
        self.变换(算子乘法运算x[1][1],算子乘法运算x[1][2])
        self.wait()
        self.play(Transform(等号左边[1],等号左边[3]))
        self.wait()
        self.变换(算子乘法运算x[1][1], 算子乘法运算x[2][0], 算子乘法运算x[1])
        # #第二部分,写作结束,所以如果有n阶求导开始.
        self.wait()
        self.play(Transform(等号右边,g["'form"][3]))
        self.wait()
        self.play(Transform(等号右边,g["'form"][4]))
        self.变换(算子乘法运算x[2][0], 算子乘法运算x[2][1])
        self.play(Transform(等号左边[1], 等号左边[4]))
        self.wait()
        self.变换(算子乘法运算x[2][1], 算子乘法运算x[2][2])
        self.play(Transform(等号左边[1],等号左边[5]))
        self.wait(3)
    def construct(self):
        timebox(self.work)
class 微分算子3(微分算子源):
    def work(self):
        #取文本
        g= self.微分算子3()
        # 布局,加上上一帧的结尾
        算子3=g["算子3"]
        散句 = 算子3["散句"]
        段落 = 算子3["段落"]
        全文 = 算子3["全文"]
        综合 = 算子3["式子"]["综合"]
        self.水印()
        self.add(g["上一帧剩余"])
        主文本=g["文本"][6]
        #段落1
        self.变换(主文本,散句["总结前文"][0],复=False)
        self.wait()
        self.变换(主文本,散句["总结前文"][1])
        self.wait()
        self.play(ShowCreationThenDestruction(g["圈圈"][2]))#g2["上一帧剩余"][2:]
        self.wait()
        self.变换(g["上一帧剩余"][2:],g["Dn公式"],复=False)
        self.wait()
        self.play(FadeOut(主文本),散句["总结前文"].shift,littleup)
        self.wait()
        self.变换(散句["总结前文"],散句["数乘分配引入"][0])
        self.wait()
        self.play(ShowCreationThenDestruction(g["圈圈"][0]),run_time=2)#g1["titles"][1]
        self.变换(g["titles"][1],g["titles"][3])
        self.wait()
        self.变换(散句["数乘分配引入"][0], 散句["数乘分配引入"][1])
        self.wait()
        self.play(Write(综合[0][0][0][-6:]))
        self.play(段落["1"].shift, littleup)
        self.wait()
        # self.play(Transform(段落["1"],散句["两函相加求导"][0])) #这个变换卡死,弄不出来++++
        #段落2
        self.play(FadeOut(段落["1"]),Write(散句["两函相加求导"][0]))
        self.wait()
        self.变换(散句["两函相加求导"][0], 散句["两函相加求导"][1])
        self.wait()
        self.变换(综合[0][0][0][-6:], 综合[1][0][0][-6:])
        self.wait()
        self.play(散句["两函相加求导"].shift, littleup)
        self.wait()
        self.变换(散句["两函相加求导"], 散句["相加求导算子定义"][0])
        self.play(ShowCreationThenDestruction(g["圈圈"][3]))
        self.wait()
        self.变换(散句["相加求导算子定义"][0], 散句["相加求导算子定义"][1])
        self.wait()
        self.变换(综合[1][0][0][-6:],综合[1][0][0][0:5])
        self.play(段落["2"].shift, littleup)
        self.wait()
        #段落3
        self.play(FadeOut(段落["2"]), Write(散句["回到第一行"][0]))
        self.play(WiggleOutThenIn(综合[0][0][0][-6:]))
        self.wait()
        self.变换(散句["回到第一行"][0], 散句["回到第一行"][1])
        self.wait()
        self.play(段落["3"][0].shift,littleup)
        self.wait()
        self.变换(段落["3"][0][1], 段落["3"][1][0])
        self.wait()
        self.变换(段落["3"][1][0], 段落["3"][1][1])
        self.wait()
        临时=综合[0][0][0][-5:-2].copy()
        self.变换(综合[0][0][0][-5:-2],综合[2][0][0][0],复=False)
        self.wait()
        self.变换(综合[0][0][0][-5:-2],临时, 复=False)
        self.wait()
        self.play(段落["3"][0:2].shift, littleup)
        self.wait()
        self.变换(段落["3"][1][1], 段落["3"][2][0])
        self.play(ShowCreationThenDestruction(g["圈圈"][3]))
        self.wait()
        self.变换(段落["3"][2][0], 段落["3"][2][1])
        self.变换(综合[0][0][0][-6:],综合[0][0][0][0:7])
        self.wait()
        self.play(段落["3"][0:3].shift, littleup)
        self.wait()
        self.变换(段落["3"][2][1], 段落["3"][3][0])
        self.wait()
        self.play(ShowCreation(g["圈圈"][4]))
        self.wait()
        self.变换(段落["3"][3][0], 段落["3"][3][1])
        self.wait()
        self.play(ShowCreation(g["圈圈"][5]))
        self.wait()
        self.play(段落["3"][0:4].shift, littleup)
        # #段落4
        self.wait()
        self.play(FadeOut(段落["3"]),FadeOut(g["圈圈"][4]),FadeOut(g["圈圈"][5]), Write(段落["4"][0][0]))
        self.wait()
        self.play(FadeOut(临时),TransformFromCopy(综合[0:2],综合[3]),FadeOut(综合[0:2]))
        self.wait()
        self.变换(段落["4"][0][0], 段落["4"][0][1])
        self.wait()
        self.play(段落["4"][0].shift, littleup)
        self.wait()
        self.变换(段落["4"][0][1], 段落["4"][1][0])
        self.wait()
        self.变换(综合[3][0][0][0:6],综合[4],综合[3][0][0][0:6])
        self.变换(综合[3][0][0][7:], 综合[5],综合[3][0][0][7:])
        self.wait()
        self.变换(段落["4"][1][0], 段落["4"][1][1])
        self.wait()
        #最终公式
        self.play(Write(综合[6]),run_time=2)
        self.wait()
        self.play(WiggleOutThenIn(综合[6][0][0][-2],scale_value=2))
        self.wait()
        self.play(Transform(综合[6],综合[9]))
        self.wait()
        self.play(WiggleOutThenIn(综合[6][0][0][8],scale_value=2),
                  WiggleOutThenIn(综合[6][0][0][18],scale_value=2),
                  WiggleOutThenIn(综合[6][0][0][32],scale_value=2),
                  )
        self.wait()
        self.play(Transform(综合[6],综合[10]))
        self.wait()
        self.play(Indicate(综合[6][0][0][1:4], scale_value=2),
                  Indicate(综合[6][0][0][11:14], scale_value=2),
                  Indicate(综合[6][0][0][26:28], scale_value=2),
                  )
        self.wait()
        self.变换(段落["4"][1][1], 段落["4"][1][2])
        self.wait()
        self.play(Transform(综合[6], 综合[7]))
        self.wait()
        self.变换(综合[6], 综合[8])
        self.wait()
        self.play(ShowCreationThenDestruction(g["圈圈"][6],run_time=2))
        self.wait()
        self.变换(综合[8], 综合[11])
        self.wait()
        self.play(ShowCreationThenDestruction(g["圈圈"][7],run_time=2))
        self.wait()
        self.变换(综合[11],算子3["最终公式"],VGroup(综合[6],综合[8]),复=False)
        self.wait()
        self.play(FadeOut(g["titles"][3]))
        self.wait(3)
    def construct(self):
        timebox(self.work)
